//
//  flv.c
//  VideoParse
//
//  Created by xiaerfei on 2019/12/6.
//  Copyright © 2019 erfeixia. All rights reserved.
//

#include "flv.h"
#include <string.h>
#include <stdlib.h>

union DOUBLE {
    double num;
    uint64_t data;
};

typedef struct {
    char *signature;
    int version;
    int video_tag;
    int audio_tag;
    unsigned int header_size;
} Flv_header;


typedef struct Tag_head {
    char type;///< 1 byte tag 类型: 音频(0x08)、视频(0x09)、script data(0x12)
    unsigned int data_size;///<  3 byte 该 tag Ddata 的大小
    unsigned int time_stamp;///<  3 byte 该 tag 的时间戳
    char time_stamp_ex;///< 1 byte 时间戳扩展字节
    unsigned int stream_id;///< 3 byte stream id
    unsigned int serial_num;
    void *value;
    struct Tag_head *next;
} Tag_header;

typedef struct Dict {
    char *key;
    void *value;
    int type;
} Dictionary;

typedef struct ScriptLink {
    int type;
    int len;
    void *value;
    struct ScriptLink *next;
} ScriptLinkList;

// 1 byte
typedef struct  {
    int sound_format;///<  音频编码类型 4 bit
    int sound_rate;///<  采样率 2 bit
    int sound_size;///<  精度 1 bit
    int sound_type;///<  类型 1 bit
    unsigned long offset;///< 数据在文件中的偏移
    unsigned long data_size;///< 数据的大小
} AudioData;
// 1 byte
typedef struct {
    int frame_type;///<  帧类型 4 bit
    int codec_id;///< 视频编码类型 4 bit
    unsigned long offset;///< 数据在文件中的偏移
    unsigned long data_size;///< 数据的大小
} VideoData;


static void seek(FILE *fp, long offset) {
    fseek(fp, offset, 0);
}

static int read(FILE *fp,char *data,long len) {
    if (fread(data, len, 1, fp) == 1) {
        return 0;
    }
    return -1;
}

//reverse_bytes - turn a BigEndian byte array into a LittleEndian integer
int reverse_bytes(char *p, int c) {
    int r = 0;
    for (int i = 0; i < c; i++)
        r |= ( ((unsigned char)(*(p+i))) << (((c-1)*8)-8*i));
    return r;
}

char *get_str_of_len(char *data, int len, int terminal) {
    if (len <= 0) { return NULL; }
    char *str = (char *)malloc(sizeof(char) * terminal ? (len + 1) : len);
    if (str == NULL) { return NULL; }
    int i = 0;
    for (; i < len; i++) {
        str[i] = data[i];
    }
    if (terminal) {
        str[i] = '\0';
    }
    return str;
}

#pragma mark - parse flv header
static int parse_flv_header(Flv_header *flv_header, char *data, int size) {
    if (data == NULL) { return -1; }
    if (data[0] != 0x46 || data[1] != 0x4c || data[2] != 0x56 ) {
        return -1;
    }
    char *signature = (char *)malloc(sizeof(char) * 4);
    signature[0] = data[0];
    signature[1] = data[1];
    signature[2] = data[2];
    signature[3] = '\0';
    flv_header->signature = signature;
    
    flv_header->version = data[3];
    
    char flags = data[4];
    /*
          5 6 7 8
     0000 0 0 0 0
     0000 0 1 0 0 audio
     0000 0 0 0 1 video
     */
    flv_header->audio_tag = (flags >> 2) & 0x01;
    flv_header->video_tag = flags & 0x01;
    
    unsigned int header_size = 0;
    
    header_size |= (unsigned char)data[8];
    header_size |= (unsigned char)data[7] << 8;
    header_size |= (unsigned char)data[6] << 16;
    header_size |= (unsigned char)data[5] << 24;
    flv_header->header_size = header_size;
    if (header_size != 9) {
        return -1;
    }
    return 0;
}
#pragma mark - parse previous tag size
static int parse_previous_tag_size(char *data,unsigned int previous_tag_size) {
    if (data == NULL) { return -1; }
    unsigned int tag_size = 0;
    
    tag_size |= (unsigned char)data[3];
    tag_size |= (unsigned char)data[2] << 8;
    tag_size |= (unsigned char)data[1] << 16;
    tag_size |= (unsigned char)data[0] << 24;
    if (tag_size == previous_tag_size) {
        return 0;
    }
    return -1;
}
#pragma mark - parse tag header
static int parse_tag_header(char *data, Tag_header *tag_header) {
    if (data == NULL) { return -1; }
    /*
     tag header 11 byte
     Type        : 1 byte tag 类型: 音频(0x08)、视频(0x09)、script data(0x12)
     DataSize    : 3 byte 该 tag Ddata 的大小
     Timestamp   : 3 byte 该 tag 的时间戳
     Timestamp_ex: 1 byte 时间戳扩展字节
     StreamID    : 3 byte stream id
     */
    tag_header->type = data[0];
    
    unsigned int data_size = 0;
    data_size |= (unsigned char)data[3];
    data_size |= (unsigned char)data[2] << 8;
    data_size |= (unsigned char)data[1] << 16;
    
    tag_header->data_size = data_size;
    
    unsigned int time_stamp_size = 0;
    time_stamp_size |= (unsigned char)data[6];
    time_stamp_size |= (unsigned char)data[5] << 8;
    time_stamp_size |= (unsigned char)data[4] << 16;
    time_stamp_size |= (unsigned char)data[7] << 24;
    
    tag_header->time_stamp = time_stamp_size;
    tag_header->time_stamp_ex = data[7];
    
    unsigned int stream_id_size = 0;
    stream_id_size |= (unsigned char)data[10];
    stream_id_size |= (unsigned char)data[9] << 8;
    stream_id_size |= (unsigned char)data[8] << 16;
    
    tag_header->stream_id = stream_id_size;
    
    return 0;
}
#pragma mark - parse Script Data结构

static void parse_script_data_link_list(ScriptLinkList *script_link_list, void *value, int type, int len) {
    if (script_link_list->type == -1) {
        script_link_list->value = value;
        script_link_list->type  = type;
        script_link_list->len   = len;
        script_link_list->next  = NULL;
    } else {
        ScriptLinkList *script = (ScriptLinkList *)malloc(sizeof(ScriptLinkList));
        script->value = value;
        script->type  = type;
        script->len   = len;
        script->next = NULL;
        script_link_list->next = script;
    }
}

static char *get_script_data_str(char *data, long *offset, int is_long) {
    int offsetn = 0;
    int l = is_long ? 4 : 2;
    int len = reverse_bytes(&data[offsetn], l);
    offsetn += l;
    char *string_data = get_str_of_len(&data[offsetn], len, 1);
    offsetn += len;
    *offset += offsetn;
    return string_data;
};

static double *get_script_data_double(char *data, long *offset) {
    double *v = (double *)malloc(sizeof(double));
    // double 8字节
    uint64_t dataValue = 0;
    int dv = 56;
    
    for (int e = 0; e < 8; e ++) {
        char dvc = data[e];
        uint64_t v = 0;
        v |= (dvc & 0xff);
        v = v << dv;
        dataValue |= v;
        dv -= 8;
    }
    union DOUBLE numDouble;
    numDouble.data = dataValue;
    *v = numDouble.num;
    *offset += 8;
    return v;
}

static char *get_script_data_bool(char *data, long *offset) {
    char *b = (char *)malloc(sizeof(char));
    *b = data[0];
    *offset += 1;
    return b;
}
static double *get_script_data_date(char *data, long *offset) {
    // double 8字节
    double *v = get_script_data_double(data, offset);
    signed int offsetn = reverse_bytes(data, 2);
    *v = *v + offsetn;
    *offset += 10;
    return v;
}

static void * parse_script_data_object(char *data, long size, ScriptLinkList *script_link_list) {
    long offset = 0;
    while ((offset + 1 + 3) <= size) {
        int type = data[offset];
        offset ++;
        switch (type) {
            case 0: {// Number type
                double *v = get_script_data_double(&data[offset], &offset);
                parse_script_data_link_list(script_link_list, v, type, 1);
                break;
            }
            case 1: {// Boolean type
                char *v = get_script_data_bool(&data[offset], &offset);
                parse_script_data_link_list(script_link_list, v, type, 1);
                break;
            }
            case 2: {// String type
                /*
                    类型(1 byte) | 长度( 2 byte) | stringData(根据长度)
                    0              1 2            3
                    SCRIPTDATASTRING
                    Field          Type     Comment
                    StringLength | UI16   | String length in bytes
                    StringData   | STRING | String data
                 */
                char *string_data = get_script_data_str(&data[offset], &offset, 0);
                parse_script_data_link_list(script_link_list, string_data, type, 1);
                break;
            }
            case 8: {// ECMA array type
                /*
                    类型(1 byte) | 长度( 4 byte) 数组长度 | 数组开始
                    SCRIPTDATAVARIABLE
                    Field          Type               Comment
                    VariableName | SCRIPTDATASTRING | Name of the variable
                    VariableData | SCRIPTDATAVALUE  | Data of the variable
                 */
                int len = reverse_bytes(&data[offset], 4);
                offset += 4;
                Dictionary *dicts = (Dictionary *)malloc(sizeof(Dictionary) * len);
                for (int i = 0; i < len; i++) {
                    dicts[i].key = get_script_data_str(&data[offset], &offset, 0);
                    int t = data[offset];
                    offset ++;
                    switch (t) {
                        case 0: {
                            dicts[i].value = get_script_data_double(&data[offset], &offset);
                            dicts[i].type = t;
                            break;
                        }
                        case 1: {
                            dicts[i].value = get_script_data_bool(&data[offset], &offset);
                            dicts[i].type = t;
                            break;
                        }
                        case 2: {
                            dicts[i].value = get_script_data_str(&data[offset], &offset, 0);
                            dicts[i].type = t;
                            break;
                        }
                        case 11: {// Date type
                            dicts[i].value = get_script_data_date(&data[offset], &offset);
                            dicts[i].type = t;
                            break;
                        }
                        case 12: {
                            dicts[i].value = get_script_data_str(&data[offset], &offset, 1);
                            dicts[i].type = t;
                            break;
                        }
                        default: {
                            offset = size;
                            break;
                        }
                    }
                }
                parse_script_data_link_list(script_link_list, dicts, type, len);
                char end = data[offset + 2];
                if (end == 0x09) {
                    return NULL;
                }
                break;
            }
            case 11: {// Date type
                double *date = get_script_data_date(&data[offset], &offset);
                parse_script_data_link_list(script_link_list, date, type, 1);
                break;
            }
            case 12: {// Long string type
                char *string_data = get_script_data_str(&data[offset], &offset, 1);
                parse_script_data_link_list(script_link_list, string_data, type, 1);
                break;
            }
            default:break;
        }
    }
    return NULL;
}
#pragma mark - parse audio
static void* parse_audio_data(char *data, long size, AudioData *audio_data) {
    /*
     1 byte
              音频编码类型          采用率       精度       类型
     +-----------------------+-----------+----------+----------+-------------------------+
     |         4 bit         |   2 bit   |  1 bit   |  1 bit   |                         |
     +---------------------------------------------------------+     SoundData           |
     |      SoundFormat      | SoundRate |SoundSize |SoundType |                         |
     +-----------------------+---------------------------------+-------------------------+

     音频编码类型：
     +---------+-----------------------------------+
     |  value  |                mark               |
     +---------------------------------------------+
     |    0    |    Linear PCM, platform endian    |
     +---------------------------------------------+
     |    1    |    ADPCM                          |
     +---------------------------------------------+
     |    2    |    MP3                            |
     +---------------------------------------------+
     |    3    |    Linear PCM, little endian      |
     +---------------------------------------------+
     |    4    |    Nellymoser 16|kHz mono         |
     +---------------------------------------------+
     |    5    |    Nellymoser 8|kHz mono          |
     +---------------------------------------------+
     |    6    |    Nellymoser                     |
     +---------------------------------------------+
     |    7    |    G.711 A|law logarithmic PCM    |
     +---------------------------------------------+
     |    8    |    G.711 mu|law logarithmic PCM   |
     +---------------------------------------------+
     |    9    |    reserved                       |
     +---------------------------------------------+
     |    10   |    AAC                            |
     +---------------------------------------------+
     |    11   |    Speex                          |
     +---------------------------------------------+
     |    14   |   MP3 8|Khz                       |
     +---------------------------------------------+
     |    15   |   Device|specific sound           |
     +---------+-----------------------------------+
     
     音频采样率：
                  Sampling rate  For AAC: always 3
     +---------+-----------------------------------+
     |  value  |                mark               |
     +---------------------------------------------+
     |    0    |              5.5|kHz              |
     +---------------------------------------------+
     |    1    |              11|kHz               |
     +---------------------------------------------+
     |    2    |              22|kHz               |
     +---------------------------------------------+
     |    3    |              44|kHz               |
     +---------------------------------------------+

     音频采样精度：
     +---------+-----------------------------------+
     |  value  |                mark               |
     +---------------------------------------------+
     |    0    |                8bit               |
     +---------------------------------------------+
     |    1    |                16bit              |
     +---------------------------------------------+
     
     音频类型：
     +---------+-----------------------------------+
     |  value  |                mark               |
     +---------------------------------------------+
     |    0    |               sndMono             |
     +---------------------------------------------+
     |    1    |               sndStereo           |
     +---------------------------------------------+
     */
    
    unsigned char sound = data[0];
    audio_data->sound_format = sound >> 4;
    /*
                0000 00 0 0
     sound_rate 0000 11 0 0
     SoundSize  0000 00 1 0
     SoundType  0000 00 0 1
     */
    audio_data->sound_rate  = (sound >> 2) & 0x03;
    audio_data->sound_size  = (sound >> 1) & 0x01;
    audio_data->sound_type  = sound & 0x01;
    
    return NULL;
}
#pragma mark - parse video
static void* parse_video_data(char *data, long size, VideoData *video_data) {
    /*
     FrameType：
     +---------+-------------------------------------------------------+
     |  value  |                        mark                           |
     +-----------------------------------------------------------------+
     |    1    |        keyframe (for AVC, a seekable frame)           |
     +-----------------------------------------------------------------+
     |    2    |     inter frame (for AVC, a non| seekable frame)      |
     +-----------------------------------------------------------------+
     |    3    |        disposable inter frame (H.263 only)            |
     +-----------------------------------------------------------------+
     |    4    |   generated keyframe (reserved for server use only)   |
     +-----------------------------------------------------------------+
     |    5    |              video info/command frame                 |
     +---------+-------------------------------------------------------+
     
     CodecID：
     +---------+--------------------------------+
     |  value  |          mark                  |
     +------------------------------------------+
     |    1    |   JPEG (currently unused)      |
     +------------------------------------------+
     |    2    |   Sorenson H.263               |
     +------------------------------------------+
     |    3    |   Screen video                 |
     +------------------------------------------+
     |    4    |   On2 VP6                      |
     +------------------------------------------+
     |    5    |   On2 VP6 with alpha channel   |
     +------------------------------------------+
     |    6    |   Screen video version 2       |
     +------------------------------------------+
     |    7    |   AVC                          |
     +---------+--------------------------------+
     */
    
    unsigned char sound = data[0];
    video_data->frame_type = sound >> 4;
    video_data->codec_id   = sound & 0x0f;
    
    return NULL;
}

#pragma mark - parse flv

static void print_flv_info(Tag_header *header) {
    printf("\n\n");
    while (header) {
        switch (header->type) {
            case 0: {
                Flv_header *flv = header->value;
                printf("============== FLV Header ==============\n");
                printf("Signature:  0x %s\n",flv->signature);
                printf("Version:    0x %X\n",flv->version);
                printf("Flags  :    0x TypeFlagsAudio: %d TypeFlagsVideo: %d\n",flv->audio_tag, flv->video_tag);
                printf("HeaderSize: 0x %X\n",flv->header_size);
                printf("========================================\n");
                free(flv->signature);
                free(flv);
                break;
            }
            case 0x08: {
                AudioData *audio_data = header->value;
                char audiotag_str[100]={0};
                printf("[Audio] %6d %6d %6d %6ld %6ld",header->serial_num,header->data_size,header->time_stamp, audio_data->offset, audio_data->data_size);
                strcat(audiotag_str,"| ");
                switch (audio_data->sound_format) {
                    case 0:strcat(audiotag_str,"Linear PCM, platform endian");break;
                    case 1:strcat(audiotag_str,"ADPCM");break;
                    case 2:strcat(audiotag_str,"MP3");break;
                    case 3:strcat(audiotag_str,"Linear PCM, little endian");break;
                    case 4:strcat(audiotag_str,"Nellymoser 16-kHz mono");break;
                    case 5:strcat(audiotag_str,"Nellymoser 8-kHz mono");break;
                    case 6:strcat(audiotag_str,"Nellymoser");break;
                    case 7:strcat(audiotag_str,"G.711 A-law logarithmic PCM");break;
                    case 8:strcat(audiotag_str,"G.711 mu-law logarithmic PCM");break;
                    case 9:strcat(audiotag_str,"reserved");break;
                    case 10:strcat(audiotag_str,"AAC");break;
                    case 11:strcat(audiotag_str,"Speex");break;
                    case 14:strcat(audiotag_str,"MP3 8-Khz");break;
                    case 15:strcat(audiotag_str,"Device-specific sound");break;
                    default:strcat(audiotag_str,"UNKNOWN");break;
                }
                strcat(audiotag_str,"| ");
                switch (audio_data->sound_rate) {
                    case 0:strcat(audiotag_str,"5.5-kHz");break;
                    case 1:strcat(audiotag_str,"1-kHz");break;
                    case 2:strcat(audiotag_str,"22-kHz");break;
                    case 3:strcat(audiotag_str,"44-kHz");break;
                    default:strcat(audiotag_str,"UNKNOWN");break;
                }
                strcat(audiotag_str,"| ");
                
                switch (audio_data->sound_size) {
                    case 0:strcat(audiotag_str,"8Bit");break;
                    case 1:strcat(audiotag_str,"16Bit");break;
                    default:strcat(audiotag_str,"UNKNOWN");break;
                }
                strcat(audiotag_str,"| ");
                
                switch (audio_data->sound_type) {
                    case 0:strcat(audiotag_str,"Mono");break;
                    case 1:strcat(audiotag_str,"Stereo");break;
                    default:strcat(audiotag_str,"UNKNOWN");break;
                }
                printf("%s\n",audiotag_str);
                free(audio_data);
                break;
            }
            case 0x09: {
                VideoData *video_data = header->value;
                printf("[Video] %6d %6d %6d %6ld %6ld",header->serial_num,header->data_size,header->time_stamp, video_data->offset, video_data->data_size);
                char videotag_str[100]={0};
                strcat(videotag_str,"| ");
                switch (video_data->frame_type) {
                    case 1:strcat(videotag_str,"key frame  ");break;
                    case 2:strcat(videotag_str,"inter frame");break;
                    case 3:strcat(videotag_str,"disposable inter frame");break;
                    case 4:strcat(videotag_str,"generated keyframe");break;
                    case 5:strcat(videotag_str,"video info/command frame");break;
                    default:strcat(videotag_str,"UNKNOWN");break;
                }
                strcat(videotag_str,"| ");
                
                switch (video_data->codec_id) {
                    case 1:strcat(videotag_str,"JPEG (currently unused)");break;
                    case 2:strcat(videotag_str,"Sorenson H.263");break;
                    case 3:strcat(videotag_str,"Screen video");break;
                    case 4:strcat(videotag_str,"On2 VP6");break;
                    case 5:strcat(videotag_str,"On2 VP6 with alpha channel");break;
                    case 6:strcat(videotag_str,"Screen video version 2");break;
                    case 7:strcat(videotag_str,"AVC");break;
                    default:strcat(videotag_str,"UNKNOWN");break;
                }
                printf("%s\n",videotag_str);
                free(video_data);
                break;
            }
            case 0x12: {
                printf("[SCRIPT] %6d %6d %6d ",header->serial_num,header->data_size,header->time_stamp);
                ScriptLinkList *link_list = header->value;
                while (link_list) {
                    switch (link_list->type) {
                        case 0: {
                            double *v = link_list->value;
                            printf("\t[Number] %f |\n",*v);
                            free(v);
                            break;
                        }
                        case 1: {
                            char *v = link_list->value;
                            printf("\t[Bool] %x |\n",*v);
                            free(v);
                            break;
                        }
                        case 2: {
                            char *v = link_list->value;
                            printf("\t[String] %s |\n",v);
                            free(v);
                            break;
                        }
                        case 8: {
                            Dictionary *dicts = link_list->value;
                            for (int i = 0; i < link_list->len; i++) {
                                switch (dicts[i].type) {
                                    case 0: {
                                        printf("\t[%s] %f |\n",dicts[i].key,*((double *)dicts[i].value));
                                        break;
                                    }
                                    case 1: {
                                        printf("\t[%s] %d |\n",dicts[i].key,*((char *)dicts[i].value));
                                        break;
                                    }
                                    case 2: {
                                        printf("\t[%s] %s |\n",dicts[i].key,((char *)dicts[i].value));
                                        break;
                                    }
                                    case 11: {// Date type
                                        printf("\t[%s] %f |\n",dicts[i].key,*((double *)dicts[i].value));
                                        break;
                                    }
                                    case 12: {
                                        printf("\t[%s] %s |\n",dicts[i].key,((char *)dicts[i].value));
                                        break;
                                    }
                                    default: {
                                        break;
                                    }
                                }
                            }
                            free(dicts);
                            break;
                        }
                        case 11: {
                            double *v = link_list->value;
                            printf("\t[Date] %f |\n",*v);
                            free(v);
                            break;
                        }
                        case 12:{
                            char *v = link_list->value;
                            printf("\t[Long String] %s |\n",v);
                            free(v);
                            break;
                        }
                        default:
                            break;
                    }
                    ScriptLinkList *next = link_list->next;
                    free(link_list);
                    link_list = next;
                }
                break;
            }
            default:
                break;
        }
        Tag_header *next = header->next;
        free(header);
        header = next;
    }
}

int flv_parse_begin(char *path) {
    if (strlen(path) == 0) { return -1; }
    unsigned long offset = 0;
    FILE *fp = fopen(path,"r");
    if (fp == NULL) {
        printf("Can't open %s",path);
        return -1;
    }
    char header[9];
    seek(fp, offset);
    if (read(fp, header, 9) == -1) {
        printf("read header error");
        return -1;
    }
    offset = 9;
    Flv_header *flv_header = (Flv_header *)malloc(sizeof(Flv_header));
    if (parse_flv_header(flv_header,header, 9) == -1) {
        return -1;
    };
    unsigned int previous_tag_size = 0, tag_count = 0;
    Tag_header *tag_header_move = (Tag_header *)malloc(sizeof(Tag_header));
    tag_header_move->type = 0;
    tag_header_move->value = flv_header;
    Tag_header *first = tag_header_move;
    while (!feof(fp)) {
        // read previous_tag UI32
        char previous_tag[4];
        if (read(fp, previous_tag, 4) == -1) {
            printf("read previous_tag error");
            break;
        }
        offset += 4;
        if (parse_previous_tag_size(previous_tag, previous_tag_size) == -1) {
            printf("tag can`t equal to previous_tag_size  error at: %d",tag_count);
            break;
        }
        // read tag
        /*
         1.read tag header 11 byte
         Type        : 1 byte tag 类型: 音频(0x08)、视频(0x09)、script data(0x12)
         DataSize    : 3 byte 该 tag Ddata 的大小
         Timestamp   : 3 byte 该 tag 的时间戳
         Timestamp_ex: 1 byte 时间戳扩展字节
         StreamID    : 3 byte stream id
         */
        Tag_header *tag_header = (Tag_header *)malloc(sizeof(Tag_header));
        tag_header->next = NULL;
        tag_header->serial_num = tag_count;
        char tag_header_data[11];
        if (read(fp, tag_header_data, 11) == -1) {
            printf("read tag_header_data error at: %d",tag_count);
            free(tag_header);
            break;
        }
        offset += 11;
        if (parse_tag_header(tag_header_data, tag_header) == -1) {
            printf("parse tag_header_data error at: %d",tag_count);
            free(tag_header);
            return -1;
        }
        if (tag_header->data_size <= 0) {
            printf("tag_header.data_size <= 0 error at: %d",tag_count);
            free(tag_header);
            break;
        }
        /*
         2.read tag data
         */
        char *tag_data = (char *)malloc(sizeof(char) * tag_header->data_size);
        if (read(fp, tag_data, tag_header->data_size) == -1) {
            printf("read tag_data error at: %d",tag_count);
            free(tag_header);
            free(tag_data);
            break;
        }
        offset += tag_header->data_size;
        switch (tag_header->type) {
            case 0x08: {// 音频
                AudioData *audio_data = (AudioData *)malloc(sizeof(AudioData));
                audio_data->data_size = tag_header->data_size - 1;
                audio_data->offset = offset + 1;
                parse_audio_data(tag_data, tag_header->data_size, audio_data);
                previous_tag_size = 11 + tag_header->data_size;
                tag_header->type = 0x08;
                tag_header->value = audio_data;
                break;
            }
            case 0x09: {// 视频
                VideoData *video_data = (VideoData *)malloc(sizeof(VideoData));
                video_data->data_size = tag_header->data_size - 1;
                video_data->offset = offset + 1;
                parse_video_data(tag_data, tag_header->data_size, video_data);
                previous_tag_size = 11 + tag_header->data_size;
                tag_header->type = 0x09;
                tag_header->value = video_data;
                break;
            }
            case 0x12: {// script data
                ScriptLinkList *script_link_list = (ScriptLinkList *)malloc(sizeof(ScriptLinkList));
                script_link_list->type = -1;
                parse_script_data_object(tag_data, tag_header->data_size,script_link_list);
                previous_tag_size = 11 + tag_header->data_size;
                tag_header->type = 0x12;
                tag_header->value = script_link_list;
                break;
            }
            default:break;
        }
        tag_header_move->next = tag_header;
        tag_header_move = tag_header;
        free(tag_data);
        tag_count ++;
    }
    
    print_flv_info(first);
    return 0;
}
// 1358127
// 1358123
